﻿using System;
using System.IO;
using FirstFloor.Documents.IO;

namespace FirstFloor.Documents.Pdf.Filters
{
    /// <summary>
    /// Represents the base class for all filters.
    /// </summary>
    internal abstract class Filter
        : Stream
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="FlateDecode"/> class.
        /// </summary>
        /// <param name="baseStream">The base stream.</param>
        /// <param name="parameters">The parameters.</param>
        public Filter(Stream baseStream, DictionaryObject parameters)
        {
            baseStream.VerifyReadAndSeek();

            this.BaseStream = baseStream;
            this.Parameters = parameters;
        }

        /// <summary>
        /// Gets access to the base stream.
        /// </summary>
        /// <value>The base stream.</value>
        protected Stream BaseStream { get; private set; }

        /// <summary>
        /// Gets the parameters associated with this filter.
        /// </summary>
        /// <value>The parameters.</value>
        public DictionaryObject Parameters { get; private set; }

        /// <summary>
        /// When overridden in a derived class, gets a value indicating whether the current stream supports reading.
        /// </summary>
        /// <value></value>
        /// <returns>true if the stream supports reading; otherwise, false.</returns>
        public sealed override bool CanRead
        {
            get { return true; }
        }

        /// <summary>
        /// When overridden in a derived class, gets a value indicating whether the current stream supports seeking.
        /// </summary>
        /// <value></value>
        /// <returns>true if the stream supports seeking; otherwise, false.</returns>
        public sealed override bool CanSeek
        {
            get { return true; }
        }

        /// <summary>
        /// When overridden in a derived class, gets a value indicating whether the current stream supports writing.
        /// </summary>
        /// <value></value>
        /// <returns>true if the stream supports writing; otherwise, false.</returns>
        public sealed override bool CanWrite
        {
            get { return false; }
        }

        /// <summary>
        /// When overridden in a derived class, clears all buffers for this stream and causes any buffered data to be written to the underlying device.
        /// </summary>
        /// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
        public sealed override void Flush()
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// When overridden in a derived class, gets the length in bytes of the stream.
        /// </summary>
        /// <value></value>
        /// <returns>A long value representing the length of the stream in bytes.</returns>
        /// <exception cref="T:System.NotSupportedException">A class derived from Stream does not support seeking. </exception>
        /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
        public abstract override long Length { get; }

        /// <summary>
        /// When overridden in a derived class, sets the length of the current stream.
        /// </summary>
        /// <param name="value">The desired length of the current stream in bytes.</param>
        /// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
        /// <exception cref="T:System.NotSupportedException">The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. </exception>
        /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
        public sealed override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
        /// </summary>
        /// <param name="buffer">An array of bytes. This method copies <paramref name="count"/> bytes from <paramref name="buffer"/> to the current stream.</param>
        /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin copying bytes to the current stream.</param>
        /// <param name="count">The number of bytes to be written to the current stream.</param>
        /// <exception cref="T:System.ArgumentException">The sum of <paramref name="offset"/> and <paramref name="count"/> is greater than the buffer length. </exception>
        /// <exception cref="T:System.ArgumentNullException">
        /// 	<paramref name="buffer"/> is null. </exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// 	<paramref name="offset"/> or <paramref name="count"/> is negative. </exception>
        /// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
        /// <exception cref="T:System.NotSupportedException">The stream does not support writing. </exception>
        /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
        public sealed override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Releases the unmanaged resources used by the <see cref="T:System.IO.Stream"/> and optionally releases the managed resources.
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (disposing) {
                // disposes the underlying base stream
                this.BaseStream.Dispose();
            }
        }

        /// <summary>
        /// Creates a stream filter for given input.
        /// </summary>
        /// <param name="filter">The filter.</param>
        /// <param name="input">The input.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        public static Filter CreateFilter(string filter, Stream input, DictionaryObject parameters)
        {
            if (filter == "FlateDecode") {
                return new FlateDecode(input, parameters);
            }

            throw new PdfParseException(Resources.UnsupportedStreamFilter, filter);
        }
    }
}
